#include <httpd.h>
#include <http_log.h>
#include <http_protocol.h>
#include <http_config.h>

#include <apr_file_info.h>
#include <apr_strings.h>

typedef struct {
    const char* file_path;
    apr_file_t* fd;
} server_guard_sconf;

typedef struct {
    int mod_switch;
} server_guard_dconf;

static void* init_server_guard_sconf(apr_pool_t* p, server_rec* s)
{
    server_guard_sconf* conf = apr_pcalloc(p, sizeof(server_guard_sconf));
    conf->file_path = NULL;
    conf->fd = NULL;
}

static void* init_server_guard_dconf(apr_pool_t* p, char* dummy)
{
    server_guard_dconf* conf = apr_pcalloc(p, sizeof(server_guard_dconf));
    return conf;
}

module AP_MODULE_DECLARE_DATA server_guard_module;

static const char* set_log_file_path(cmd_parms* cmd, void* dummy, const char* val)
{
    server_guard_sconf* conf = 
        ap_get_module_config(cmd->server->module_config, &server_guard_module);
    conf->file_path = val;
    return NULL;
}

static const command_rec server_guard_cmds[] = {
    AP_INIT_FLAG("ServerGuard", ap_set_flag_slot,
        (void*)APR_OFFSETOF(server_guard_dconf, mod_switch), ACCESS_CONF,
        "Enable server guard"),
    AP_INIT_TAKE1("ServerGuardLogPath", set_log_file_path, NULL, RSRC_CONF,
        "Set server guard log path"),
    { NULL }
};

static apr_file_t* do_open_log(const char* path, apr_pool_t* p)
{
    if (*path == '|') {
        piped_log *pl;

        pl = ap_open_piped_log(p, path + 1);
        if (pl == NULL) {
           return NULL;
        }
        return ap_piped_log_write_fd(pl);
    }

    apr_file_t* fd;
    apr_status_t ret = apr_file_open(&fd, path, 
        (APR_WRITE | APR_APPEND | APR_CREATE | APR_LARGEFILE), APR_OS_DEFAULT, p);

    if (ret != APR_SUCCESS) return NULL;
    return fd;
}

static int open_log_handler(apr_pool_t* pconf, apr_pool_t* plog, apr_pool_t* ptemp, server_rec* s)
{
    server_guard_sconf* conf = 
        ap_get_module_config(s->module_config, &server_guard_module);

    if (conf->file_path != NULL)
        conf->fd = do_open_log(conf->file_path, plog);

    for (s = s->next; s; s = s->next) {
        server_guard_sconf* conf =
            ap_get_module_config(s->module_config, &server_guard_module);
        if (conf->file_path != NULL)
            conf->fd = do_open_log(conf->file_path, plog);
    }

    return OK;
}

static int server_guard_handler(request_rec* r)
{
    server_guard_dconf* dconf =
        ap_get_module_config(r->per_dir_config, &server_guard_module);

    if (!dconf->mod_switch || r->main != NULL)
    {
        return DECLINED;
    }

    server_guard_sconf* sconf = 
        ap_get_module_config(r->server->module_config, &server_guard_module);

    if (sconf->file_path == NULL)
        return OK;

    // request time
    apr_time_exp_t req_time;
    ap_explode_recent_localtime(&req_time, r->request_time);

    apr_size_t retsize;
    char req_time_str[128];
    apr_strftime(req_time_str, &retsize, sizeof(req_time_str), "[%Y-%m-%d %H:%M:%S]", &req_time);

    // others
    char* conn_id = apr_ltoa(r->pool, r->connection->id);
    char* remote_ip = r->connection->remote_ip;
    char* method = ap_escape_logitem(r->pool, r->method);
    char* uri = ap_escape_logitem(r->pool, r->uri);

    // do log
    char* sep = " ";
    char* log_msg = apr_pstrcat(r->pool, 
        conn_id, sep, remote_ip, sep, req_time_str, sep, method, sep, uri, "\n", NULL);
    apr_file_puts(log_msg, sconf->fd);

    //if (sconf->fd) return HTTP_FORBIDDEN;*/
    return OK;
}

static void server_guard_hooks(apr_pool_t* pool)
{
    ap_hook_open_logs(open_log_handler, NULL, NULL, APR_HOOK_MIDDLE);
    ap_hook_fixups(server_guard_handler, NULL, NULL, APR_HOOK_MIDDLE);
}

module AP_MODULE_DECLARE_DATA server_guard_module = {
    STANDARD20_MODULE_STUFF,
    init_server_guard_dconf,
    NULL,
    init_server_guard_sconf,
    NULL,
    server_guard_cmds,
    server_guard_hooks
};
